#include "test.h"

void runTests(ArgStruct *args) {

    int m;
    m = args->m; 

     //////////
    // Testing
     //////////

    printf("m = %d ", m);
    Vector *poly;
    Matrix *vector_space;
    Matrix *add_one;

    printf("Generating primitive polynomial...\n");
    poly = gf_bin_primpoly(m);
    printf("Primitive polynomial: ");
    print_vector(poly);
    
    printf("\nGenerating vector space representation...\n");
    vector_space = gf_vector(m,poly);
    printf("Vector space:\n");
    print_matrix(vector_space);
    printf("\n");
    
    printf("\nGenerating add-one table (Zech's Logarithms)...\n");
    add_one = gf_add_one(m,vector_space);
    
    printf("Add-one table:\n");
    print_matrix(add_one); 
    
    printf("\nTesting GF element functions...\n");

    printf("Computing elementMult(3,4)...\n");
    printf("Answer: %i\n",elementMult(m, add_one, 3,4));

    printf("Computing elementMult(-1, 5)...\n");
    printf("Answer: %i\n",elementMult(m, add_one,-1,5));

    printf("Computing elementMult(2, 3)...\n");
    printf("Answer: %i\n",elementMult(m, add_one,2,3));

    printf("Computing elementMult(3, 0)...\n");
    printf("Answer: %i\n",elementMult(m, add_one,3,0));

    Vector *X;
    
    X = allocate_vector(3);
    X->data[0] = 1;
    X->data[1] = 4;
    X->data[2] = 11;
    
    printf("Computing elementReduce([1 4 11])...\n");
    printf("Answer: %i\n",elementReduce(m, add_one, X));

    free_vector(X);
    X = allocate_vector(3);

    X->data[0] = 5;
    X->data[1] = 5;
    X->data[2] = 5;

    printf("Computing elementReduce([5 5 5])...\n");
    printf("Answer: %i\n",elementReduce(m, add_one, X));

    free_vector(X);
    X = allocate_vector(4);
    X->data[0] = 2;
    X->data[1] = 2;
    X->data[2] = 2;
    X->data[3] = 2;

    printf("Computing elementReduce([2 2 2 2])...\n");
    printf("Answer: %i\n",elementReduce(m, add_one, X));
   
    free_vector(X);
    X = allocate_vector(1);
    X->data[0] = 0;
   
    printf("Computing elementReduce([0])...\n");
    printf("Answer: %i\n",elementReduce(m, add_one, X));

    free_vector(X);
    X = allocate_vector(2);
    X->data[0] = 2;
    X->data[1] = 3;

    printf("Computing elementReduce([2,3])...\n");
    printf("Answer: %i\n",elementReduce(m, add_one, X));
 
    free_vector(X);
    X = allocate_vector(1);
    X->data[0] = -1;

    printf("Computing isZero([-1])...\n");
    printf("Answer: %i\n",isZero(m, add_one, X));

    X->data[0] = 3;

    printf("Computing isZero([3])...\n");
    printf("Answer: %i\n",isZero(m, add_one, X));

    printf("Computing elementAdd(2,3)...\n");
    printf("Answer: %i\n",elementAdd(m, add_one, 2, 3));

    printf("Computing elementAdd(3,10)...\n");
    printf("Answer: %i\n", elementAdd(m, add_one, 3, 10));

    printf("Computing elementDiv(4,2)...\n");
    printf("Answer: %i\n", elementDiv(m, add_one, 4, 2));

    printf("Computing elementDiv(2,5)...\n");
    printf("Answer: %i\n", elementDiv(m, add_one, 2,5));

    printf("Computing elementPow(2,0)...\n");
    printf("Answer: %i\n", elementPow(m, add_one, 2, 0));

    printf("Computing elementPow(-1,2)...\n");
    printf("Answer: %i\n", elementPow(m, add_one, -1, 2));

    printf("Computing elementPow(0,2)...\n");
    printf("Answer: %i\n", elementPow(m, add_one, 0, 2));

    printf("Computing elementPow(3,5)...\n");
    printf("Answer: %i\n", elementPow(m, add_one, 3, 5));


    printf("\n\nTesting polynomial functions...\n");
    
    free_vector(X);
    X = allocate_vector(1);
    X->data[0] = -1;
    
    printf("Computing polyIsZero([-1])...\n");
    printf("Answer: %i\n", polyIsZero(X));
    
    free_vector(X);
    X = allocate_vector(4);
    X->data[0] = -1;
    X->data[1] = -1;
    X->data[2] = -1;
    X->data[3] = -1;

    printf("Computing polyIsZero([-1 -1 -1 -1])...\n");
    printf("Answer: %i\n", polyIsZero(X));

    X->data[1] = 3;
    printf("Computing polyIsZero([-1 3 -1 -1])...\n");
    printf("Answer: %i\n", polyIsZero(X));

    printf("Computing polyTrimZeros([-1 3 -1 -1])...\n");
    Vector* Z;
    Z = polyTrimZeros(X);
    printf("Answer: ");
    print_vector(Z);

    free_vector(X);
    free_vector(Z);
    X = allocate_vector(3);
    X->data[0] = 2;
    X->data[1] = -1;
    X->data[2] = 1;
    
    printf("Computing polyTrimZeros([2 -1 1])...\n");
    Z = polyTrimZeros(X);
    printf("Answer: ");
    print_vector(Z);

    free_vector(X);
    free_vector(Z);
    X = allocate_vector(3);
    X->data[0] = -1;
    X->data[1] = -1;
    X->data[2] = 0;
    
    printf("Computing polyPlugIn([-1 -1 0], 3)...\n");
    printf("Answer: %i\n",polyPlugIn(m, add_one, X, 3));

    Vector * Y;

    free_vector(X);

    X = allocate_vector(4);
    Y = allocate_vector(3);
    
    X->data[0] = 0;
    X->data[1] = 1;
    X->data[2] = 2;
    X->data[3] = 3;

    Y->data[0] = 2;
    Y->data[1] = 0;
    Y->data[2] = 1;

    printf("Computing polyAdd([0 1 2 3], [2 0 1])...\n");
    printf("Answer: ");
    Z = polyAdd(m, add_one, X, Y);
    print_vector(Z);

    free_vector(X);
    free_vector(Y);
    free_vector(Z);
    
    X = allocate_vector(4);
    Y = allocate_vector(3);

    X->data[0] = 0;
    X->data[1] = 1;
    X->data[2] = 2;
    X->data[3] = 3;

    Y->data[0] = 2;
    Y->data[1] = 0;
    Y->data[2] = 1;

    printf("Computing polyMult([0 1 2 3], [2 0 1])...\n");
    printf("Answer: ");
    Z = polyMult(m, add_one, X, Y);
    print_vector(Z);

    free_vector(X);
    free_vector(Y);
    free_vector(Z);

    X = allocate_vector(2);
    X->data[0] = -1;
    X->data[1] = 5;

    Y = allocate_vector(2);
    Y->data[0] = 2;
    Y->data[1] = 4;

    printf("Computing polyMult([-1 5], [2 4])...\n");
    printf("Answer: ");
    Z = polyMult(m, add_one, X, Y);
    print_vector(Z);

    free_vector(X);
    free_vector(Y);
    free_vector(Z);

    X = allocate_vector(4);
    Y = allocate_vector(3);

    X->data[0] = -1;
    X->data[1] = 43;
    X->data[2] = 2;
    X->data[3] = 6;

    Y->data[0] = 5;
    Y->data[1] = 2;
    Y->data[2] = 3;

    printf("Computing polyMult([-1 43 2 6], [5 2 3])...\n");
    printf("Answer: ");
    Z = polyMult(m, add_one, X, Y);
    print_vector(Z);
    free_vector(Z);

    X->data[0] = -1;
    X->data[1] = 43;
    X->data[2] = 2;
    X->data[3] = 6;

    Y->data[0] = 5;
    Y->data[1] = 2;
    Y->data[2] = 3;

    printf("Computing deg([-1 43 2 6])...\n");
    printf("Answer: %d\n", deg(X));
    printf("Computing deg([5 2 3])...\n");
    printf("Answer: %d\n", deg(Y));

    X->data[0] = 7;
    X->data[1] = 43;
    X->data[2] = -1;
    X->data[3] = -1;

    printf("Computing deg([7 43 -1 -1])...\n");
    printf("Answer: %d\n", deg(X));

    free_vector(X);
    X = allocate_vector(6);
    X->data[0] = 1;
    X->data[1] = 2;
    X->data[2] = 3;
    X->data[3] = 4;
    X->data[4] = 5;
    X->data[5] = 6;

    printf("Computing findRoots([1 2 3 4 5 6])...\n");
    printf("Answer: ");
    Z = findRoots(m, add_one, X);
    
    if (Z != NULL) {
        print_vector(Z);
        free_vector(Z);
    }
    else {
        printf("No roots\n");
    }

    free_vector(X);
    X = allocate_vector(4);
    X->data[0] = 1;
    X->data[1] = 3;
    X->data[2] = 5;
    X->data[3] = 6;

    printf("Computing findRoots([1 3 5 6])...\n");
    printf("Answer: ");

    Z = findRoots(m, add_one, X);
    
    if (Z != NULL) {
        print_vector(Z);
        free_vector(Z);
    }
    else {
        printf("No roots\n");
    }

    X->data[0] = 1;
    X->data[1] = 2;
    X->data[2] = 5;
    X->data[3] = 6;

    printf("Computing findRoots([1 2 5 6])...\n");
    printf("Answer: ");

    Z = findRoots(m, add_one, X);
    
    if (Z != NULL) {
        print_vector(Z);
        free_vector(Z);
    }
    else {
        printf("No roots\n");
    }

    X->data[0] = 5;
    X->data[1] = 2;
    X->data[2] = 5;
    X->data[3] = 4;
    
    printf("Computing findRoots([5 2 5 4])...\n");
    printf("Answer: ");
    Z = findRoots(m, add_one, X);
    if (Z != NULL) {
        print_vector(Z);
        free_vector(Z);
    }
    else {
        printf("No roots\n");
    }

    Vector **result;
    free_vector(X);
    X = allocate_vector(1);

    X->data[0] = -1;
    printf("Computing polyDiv([-1], [5 2 3])...\n");
    printf("Answer: ");
    result = polyDiv(m, add_one, X, Y);
    printf("Quotient: ");
    print_vector(result[0]);
    printf("Remainder: ");
    print_vector(result[1]);

    free_vector(result[0]);
    free_vector(result[1]);
    free(result);


    printf("Computing polyDiv([5 2 3], [-1])...\n");
    printf("Answer: ");
    result = polyDiv(m, add_one, Y, X);
    printf("Quotient: ");
    print_vector(result[0]);
    printf("Remainder: ");
    print_vector(result[1]);
    free_vector(result[0]);
    free_vector(result[1]);
    free(result);

    free_vector(X);
    X = allocate_vector(3);
    X->data[0] = 5;
    X->data[1] = 2;
    X->data[2] = 1;

    Y->data[0] = 3;
    Y->data[1] = 4;
    Y->data[2] = 5;
    printf("Computing polyDiv([5 2 1], [3 4 5])...\n");
    result = polyDiv(m, add_one, X, Y);
    printf("Quotient: ");
    print_vector(result[0]);
    printf("Remainder: ");
    print_vector(result[1]);

    free_vector(result[0]);
    free_vector(result[1]);
    free(result);

    printf("Computing polyDiv([3 4 6], [5 2 1])...\n");
    result = polyDiv(m, add_one, Y, X);
    printf("Quotient: ");
    print_vector(result[0]);
    printf("Remainder: ");
    print_vector(result[1]);

    free_vector(result[0]);
    free_vector(result[1]);
    free(result);

    X->data[0] = 3;
    X->data[1] = 4;
    X->data[2] = 5;
    printf("Computing polyDiv([3 4 5], [3 4 5])...\n");
    result = polyDiv(m, add_one, X, Y);
    printf("Quotient: ");
    print_vector(result[0]);
    printf("Remainder: ");
    print_vector(result[1]);

    free_vector(result[0]);
    free_vector(result[1]);
    free(result);

    free_vector(X);
    X = allocate_vector(4);
    X->data[0] = 1;
    X->data[1] = 2;
    X->data[2] = 3;
    X->data[3] = 4;
    printf("Computing polyDiv([1 2 3 4], [3 4 5])...\n");
    result = polyDiv(m, add_one, X, Y);
    printf("Quotient: ");
    print_vector(result[0]);
    printf("Remainder: ");
    print_vector(result[1]);

    free_vector(result[0]);
    free_vector(result[1]);
    free(result);

    printf("Computing polyDiv([3 4 5], [1 2 3 4])...\n");
    result = polyDiv(m, add_one, Y, X);
    printf("Quotient: ");
    print_vector(result[0]);
    printf("Remainder: ");
    print_vector(result[1]);

    free_vector(result[0]);
    free_vector(result[1]);
    free(result);

    // Testing Forney
    Vector *rts, *rts_inv, *r1, *t1;

    rts= allocate_vector(2);
    rts->data[0] = 2;
    rts->data[1] = 3;	

    rts_inv = allocate_vector(2);
    rts_inv->data[0] = 5;
    rts_inv->data[1] = 4;

    r1 = allocate_vector(3);
    r1->data[0] = 0;
    r1->data[1] = 5;
    r1->data[2] = 6;

    t1 = allocate_vector(3);
    t1->data[0] = 0;
    t1->data[1] = 0;
    t1->data[2] = 2;

    printf("Computing forney:\n");
    printf("Answer: %d\n",forney(m, add_one, rts, rts_inv, 1, r1, t1));

    rts->data[0] = 2;
    rts->data[1] = 3;
    rts_inv->data[0] = 5;
    rts_inv->data[1] = 4;
    r1->data[0] = 0;
    r1->data[1] = 5;
    r1->data[2] = 6;
    t1->data[0] = 0;
    t1->data[1] = 0;
    t1->data[2] = 2;

    printf("Computing forney:\n");
    printf("Answer: %d\n",forney(m, add_one, rts, rts_inv, 2, r1, t1));

    free_vector(rts);
    free_vector(rts_inv);
    free_vector(r1);
    free_vector(t1);

    // Testing Euclid
    Vector *T;
    T = allocate_vector(7);
    T->data[0] = 4;
    T->data[1] = 6;
    T->data[2] = 7;
    T->data[3] = 6;
    T->data[4] = 1;
    T->data[5] = 6;
    T->data[6] = 0;
    Z = euclid(m, add_one, 2, T);
    free_vector(T);
    printf("Computing Euclid: \n");
    printf("Answer: ");
    print_vector(Z);
    free_vector(Z);

    // parallelTowers test (pipelined version of Euclid)
    Vector** codeArray;
    int i;

    codeArray = (Vector**)malloc(sizeof(Vector*)*5);

    for(i=0; i<5;i++){
      codeArray[i] = allocate_vector(7);
      codeArray[i]->data[0] = 2;
      codeArray[i]->data[1] = 0;
      codeArray[i]->data[2] = 2;
      codeArray[i]->data[3] = 3;
      codeArray[i]->data[4] = 3;
      codeArray[i]->data[5] = 0;
      codeArray[i]->data[6] = 4;
    }
    printf("Testing pipelined version of Euclid (parallelTowers)...\n");
    result = parallelTowers(m, add_one, 2, 5, codeArray);
    printf("Decoded words...\n");
    for(i=0;i<5;i++){
      print_vector(result[i]);
      free_vector(result[i]);
      free_vector(codeArray[i]);
    }
    free(codeArray);
    free(result);

    // Decoder test
	 printf("Testing decoders ...\n");
//    serialDecoder(m, add_one, 2, "newData", "serialData");
//    openMPDecoder(m, add_one, 2, 5, "newData", "parallel2Data");
//   pthreadsDecoder(m, add_one, 2, "newData", "parallel3Data");
    
    // Test of RS Generator and encoder
    printf("Testing RS_generator and encoder...\n");
    Z = RS_generator(m, add_one, args->t);
    printf("m = %i, t = %i\n",args->m, args->t);
    printf("Generator polynomial: ");
    print_vector(Z);

    X = allocate_vector(3);
    X->data[0] = 0;
    X->data[1] = 5;
    X->data[2] = 1;

    free_vector(Y);
    Y = RS_encoder(m, add_one, args->t, X, Z);
    printf("Encoding message [0 5 1].  Result is: ");
    print_vector(Y);


    printf("\nFreeing all allocated memory...\n");
    free_vector(X);
    free_vector(Y);
    free_vector(Z);
    free_vector(poly);
    free_matrix(vector_space);
    free_matrix(add_one);


    return;

}
